package handler

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/keepfoo/minirest/db"
	"github.com/keepfoo/minirest/logger"
)

func GetHandler() func(w http.ResponseWriter, r *http.Request) {
	return RestHandler(func(args map[string]interface{}) map[string]interface{} {
		return NewSQLParseContext(args).getResponse()
	})
}

type TableGetResult struct {
	Code  int         `json:"code"`
	Msg   string      `json:"msg"`
	Count int         `json:"count"`
	Data  interface{} `json:"data"`
}

func TableGetHandler(w http.ResponseWriter, r *http.Request) {
	uri := r.RequestURI
	table := uri[5:strings.Index(uri, "?")]
	if _, exists := db.AllTable[table]; !exists {
		RenderJson(w, TableGetResult{Code: http.StatusBadRequest, Msg: table + " 表不存在"})
		return
	}
	err := r.ParseForm()
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	page, err := strconv.ParseFloat(r.Form["page"][0], 32)
	size, err := strconv.ParseFloat(r.Form["limit"][0], 32)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	key := table + "[]"
	reqMap := map[string]interface{}{
		key: map[string]interface{}{
			"page":      page,
			"size":      size,
			"withCount": true,
			table:       map[string]interface{}{},
		},
	}
	response := NewSQLParseContext(reqMap).getResponse()
	RenderJson(w, TableGetResult{Code: 0, Count: response[key+"#count"].(int), Data: response[key]})
}

type SQLParseContext struct {
	req           map[string]interface{}
	resp          map[string]interface{}
	waitKeys      map[string]bool
	completedKeys map[string]bool
	time          map[string]int64
	end           bool
	withCount     bool
}

func NewSQLParseContext(reqMap map[string]interface{}) *SQLParseContext {
	logger.Debugf("NewSQLParseContext %v", reqMap)
	return &SQLParseContext{
		req:           reqMap,
		resp:          make(map[string]interface{}),
		waitKeys:      make(map[string]bool),
		completedKeys: make(map[string]bool),
		time:          make(map[string]int64),
	}
}

func (c *SQLParseContext) getResponse() map[string]interface{} {
	startTime := time.Now().Nanosecond()
	for key := range c.req {
		if !c.completedKeys[key] {
			c.parseResponse(key)
			if c.end {
				return c.resp
			}
		}
	}
	c.resp["time"] = fmt.Sprintf("%dms|%v", (time.Now().Nanosecond()-startTime)/1000000, c.time)
	return c.resp
}

func (c *SQLParseContext) parseResponse(key string) {
	startTime := time.Now().UnixNano()

	c.waitKeys[key] = true
	logger.Debugf("开始解析 %s", key)
	if c.req[key] == nil {
		c.End(http.StatusBadRequest, "值不能为空, key: "+key)
		return
	}
	if fieldMap, ok := c.req[key].(map[string]interface{}); !ok {
		c.End(http.StatusBadRequest, "值类型不对，只支持 Object 类型")
	} else {
		parseObj := db.SQLParseObject{LoadFunc: c.queryResp}
		if c.end {
			return
		}
		if err := parseObj.From(key, fieldMap); err != nil {
			c.End(http.StatusBadRequest, err.Error())
			return
		} else {
			sql := parseObj.SelectSQL()
			logger.Debugf("解析 %s 执行SQL: %s %v", key, sql, parseObj.Values)
			if parseObj.QueryFirst {
				c.resp[key], err = db.QueryOne(sql, parseObj.Values...)
			} else {
				c.resp[key], err = db.QueryAll(sql, parseObj.Values...)
			}
			if parseObj.WithCount {
				c.resp[key+"#count"] = db.QueryCount(parseObj.CountSQL(), parseObj.Values...)
			}
			if err != nil {
				c.End(http.StatusInternalServerError, err.Error())
			} else {
				c.resp["code"] = http.StatusOK
			}
		}
	}
	c.waitKeys[key] = false
	c.time[key] = time.Now().UnixNano() - startTime
}

// 查询已知结果
func (c *SQLParseContext) queryResp(queryString string) interface{} {
	var paths []string
	qs := strings.TrimSpace(queryString)
	if strings.HasPrefix(qs, "/") {
		paths = strings.Split(qs[1:], "/")
	} else {
		paths = strings.Split(queryString, "/")
	}
	var targetValue interface{}
	for _, x := range paths {
		if targetValue == nil {
			if c.waitKeys[x] {
				c.End(http.StatusBadRequest, "关联查询有循环依赖，queryString: "+queryString)
				return nil
			} else if c.completedKeys[x] {
				targetValue = c.resp[x]
			} else {
				c.parseResponse(x)
				targetValue = c.resp[x]
			}
		} else {
			targetValue = targetValue.(map[string]interface{})[x]
		}
		if targetValue == nil {
			c.End(http.StatusBadRequest, fmt.Sprintf("关联查询未发现相应值，queryString: %s", queryString))
		}
	}
	return targetValue
}

func (c *SQLParseContext) End(code int, msg string) {
	c.resp["code"] = code
	c.resp["msg"] = msg
	c.end = true
	logger.Errorf("发生错误，终止处理, code: %d, msg: %s", code, msg)
}
